/**
 * This file is part of the CAKIS project.
 * 
 * Copyright (c) 2009-2010 - Florian Arndt, Christian Kerl
 * 
 * @license GNU Lesser General Public License v3 <http://www.opensource.org/licenses/lgpl-3.0.html>
 * @since   01.03.2010
 * @version SVN: $Id: ProductListHome.java 164 2010-05-19 15:34:30Z christian-kerl@web.de $
 */

package org.cakis.server.action.impl;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.persistence.Query;

import org.cakis.server.Names;
import org.cakis.server.model.ProductList;
import org.cakis.server.model.ProductListEntry;
import org.cakis.server.model.Recipe;
import org.cakis.server.model.RecipeIngredient;
import org.cakis.server.service.StockService;
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.Begin;
import org.jboss.seam.annotations.Factory;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Logger;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Out;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.annotations.datamodel.DataModel;
import org.jboss.seam.annotations.datamodel.DataModelSelection;
import org.jboss.seam.annotations.web.RequestParameter;
import org.jboss.seam.framework.EntityHome;
import org.jboss.seam.log.Log;

@Name("productListHome")
@Scope(ScopeType.CONVERSATION)
public class ProductListHome extends EntityHome<ProductList> {
    private static final long serialVersionUID = 1L;

    @Logger
    private Log log;

    @In(Names.StockService)
    private StockService.Local stockService;

    @RequestParameter
    private Long productListId;

    @DataModelSelection
    private ProductListEntry selectedProductListEntry;

    @Out(required = false)
    private ProductListEntry toEditProductListEntry;

    private List<ProductListEntry> toDeleteProductListEntries = new ArrayList<ProductListEntry>();

    private ProductListEntry newProductListEntry;

    private List<Recipe> recipes;
    private Recipe toAddRecipe;

    public List<Recipe> getRecipes() {
        if(recipes == null) {
            // TODO: hack, should be encapsulated in repository package, but this breaks <s:convertEntity /> because
            // in the repository component a different entity manager is used, this is causing jsf validation error
            // alternative: override hashCode and equals
            Query q = getEntityManager().createQuery("select recipe from Recipe recipe order by recipe.name");
            recipes = q.getResultList();
        }
        return recipes;
    }

    public Recipe getToAddRecipe() {
        return toAddRecipe;
    }

    public void setToAddRecipe(Recipe toAddRecipe) {
        this.toAddRecipe = toAddRecipe;
    }

    @Override
    public Object getId()
    {
        if (productListId == null)
        {
            return super.getId();
        }
        else
        {
            return productListId;
        }
    }

    @DataModel
    @Factory
    public List<ProductListEntry> getListEntries() {
        if(instance != null) {
            List<ProductListEntry> result = new ArrayList<ProductListEntry>();
            result.addAll(instance.getEntries());
            if(newProductListEntry != null) {
                result.add(newProductListEntry);
            }
            return result;
        } else {
            return null;
        }
    }

    @Override @Begin
    public void create() {
        super.create();
    }

    @Override
    public String persist() {
        endEdit();

        toDeleteProductListEntries.clear();

        return super.persist();
    }

    @Override
    public String update() {
        endEdit();

        for(ProductListEntry entry : toDeleteProductListEntries) {
            getEntityManager().remove(reattach(entry));
        }
        toDeleteProductListEntries.clear();

        return super.update();
    }

    private void mergeWithExistingProductListEntries() {
        boolean delete = false;
        ProductListEntry entry = toEditProductListEntry;

        for(ProductListEntry e : instance.getEntries()) {
            if(e != entry && e.getProduct().getId() == entry.getProduct().getId()) {
                e.setCount(e.getCount() + entry.getCount());
                delete = true;
                break;
            }
        }

        if(delete) {
            deleteProductListEntry();
        }
    }

    private void addEntryToList(ProductList list, ProductListEntry entry) {
        entry.setProductList(list);
        list.getEntries().add(entry);
    }

    @Override
    protected ProductList createInstance() {
        ProductList result = super.createInstance();
        result.setName(getGeneratedName());

        return result;
    }

    private String getGeneratedName() {
        // TODO: DateFormat should be dependend on I18N
        return "ProductList " + new SimpleDateFormat("dd.MM.yyyy").format(new Date());
    }

    public void addProductListEntriesForRecipe() {
        log.info("Selected Recipe #0", toAddRecipe != null ? toAddRecipe.getName() : "<null>");

        // TODO: implement somewhere else

        for(RecipeIngredient ingredient : toAddRecipe.getIngredients()) {
            ProductListEntry missing = stockService.getMissingNumberOfStockItemsFor(ingredient.getProduct(), ingredient.getQuantity());

            if(missing.getCount() > 0) {
                addProductListEntry();

                newProductListEntry.setProduct(missing.getProduct());
                newProductListEntry.setCount(missing.getCount());

                saveEditProductListEntry();
            }
        }
    }

    public void addProductListEntry() {
        resetNewProductListEntry();

        newProductListEntry = new ProductListEntry();
        newProductListEntry.setCount(0);

        beginEdit(newProductListEntry);
    }

    public void editProductListEntry() {
        resetNewProductListEntry();

        beginEdit(selectedProductListEntry);
    }

    public void deleteProductListEntry() {
        if(selectedProductListEntry.getId() != null) {
            toDeleteProductListEntries.add(selectedProductListEntry);
        }
        instance.getEntries().remove(selectedProductListEntry);
    }

    public void saveEditProductListEntry() {
        if(newProductListEntry != null) {
            addEntryToList(instance, newProductListEntry);
        }

        mergeWithExistingProductListEntries();
        endEdit();
    }

    public void cancelEditProductListEntry() {
        endEdit();
    }

    private void beginEdit(ProductListEntry entry) {
        toEditProductListEntry = entry;
    }

    private void endEdit() {
        resetNewProductListEntry();
        toEditProductListEntry = null;
    }

    private void resetNewProductListEntry() {
        newProductListEntry = null;
    }

    private <T> T reattach(T object) {
        return getEntityManager().merge(object);
    }
}
